﻿/*
 * Copyright © 2011 Tristan Reeves, treeves@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using Sprinkles.Secondary;

namespace Sprinkles.Filters
{
    public class AssemblyScanningDeploymentHostProvider : IDeploymentHostProvider
    {
        private bool m_ReadAttributeBasedDeployments = false;
        private bool m_ReadDeploymentHosts = true;
        private const string APPSETTINGS_KEY = "sprinkles.assemblies";
        private const string ASSEMBLY_PLACEHOLDER = "${" + APPSETTINGS_KEY + "}";
        private string[] m_AssembliesToScan;
        private void UpdateAssembliesToScan()
        {
            m_AssembliesToScan = m_AssembliesToScan ?? new string[0];
            if (m_AssembliesToScan.Contains(ASSEMBLY_PLACEHOLDER))
            {
                m_AssembliesToScan = m_AssembliesToScan.Except(new[] { ASSEMBLY_PLACEHOLDER }).ToArray();
                var newAssemblies = (ConfigurationManager.AppSettings[APPSETTINGS_KEY] ?? "")
                    .Trim()
                    .Split(';')
                    .Where(x => x != null)
                    .Select(x => x.Trim())
                    .Where(x => x != "")
                    .Distinct();

                m_AssembliesToScan = m_AssembliesToScan
                    .Union(newAssemblies)
                    .Distinct()
                    .ToArray();
            }
        }
        private bool AllowAssembly(Assembly ass)
        {
            if (ass == Assembly.GetExecutingAssembly()) return false;
            if (ass.FullName.Contains("Sprinkles.Hook")) return false;

            // Iterate through the attributes for the assembly.
            foreach (Attribute attr in Attribute.GetCustomAttributes(ass))
            {
                // Check for the AssemblyTitle attribute.
                if (attr is AssemblyCompanyAttribute)
                {
                    //TODO: make this generic somehow
                    var company = ((AssemblyCompanyAttribute)attr).Company ?? "";
                    company = company.ToUpper().Trim();
                    if (company.Contains("MICROSOFT")) return false;
                    if (company.Contains("JETBRAINS")) return false;
                    if (company.Contains("NUNIT")) return false;
                    if (company.Contains("SPRINGSOURCE")) return false;
                    if (company.Contains("TELERIK")) return false;
                    if (company.Contains("NETCOMMON.SF")) return false;
                }
            }
            return true;
        }
        private IEnumerable<Assembly> ScanLoadedAssemblies()
        {
            var result = AppDomain.CurrentDomain.GetAssemblies().ToArray();
            result = result.Where(AllowAssembly).ToArray();
            return result;
        }

        public IEnumerable<IDeploymentHost> GetDeploymentHosts()
        {
            UpdateAssembliesToScan();

            var assemblies = (AssembliesToScan ?? new string[0])
                .Select(a => (a ?? "").Trim())
                .Where(a => a != "")
                .Distinct()
                .Select(a => { try { return Assembly.Load(a); } catch { return null; } })
                .Where(a => a != null)
                .Where(AllowAssembly)
                .ToArray();

            if (Autoscan)
            {
                assemblies = assemblies.Concat(ScanLoadedAssemblies()).Distinct().ToArray();
            }

            if (m_ReadAttributeBasedDeployments)
            {
                var attributeReadingHost = new DefaultAttributeReadingDeploymentHost();
                attributeReadingHost.Assemblies = assemblies;
                yield return attributeReadingHost;
            }
            if (m_ReadDeploymentHosts)
            {
                foreach (var a in assemblies)
                {
                    foreach (var t in a.GetTypes())
                    {
                        IDeploymentHost dh = null;
                        if (t == null) continue;
                        if (!typeof(IDeploymentHost).IsAssignableFrom(t)) continue;
                        var ctr = t.GetConstructor(Type.EmptyTypes);
                        try
                        {
                            dh = ctr.Invoke(null) as IDeploymentHost;
                        }
                        catch (Exception ex)
                        {
                            throw new InvalidOperationException(
                                "Could not invoke default constructor for [" + t.FullName + "]", ex);
                        }
                        yield return dh;
                    }
                }
            }
        }
        public string[] AssembliesToScan
        {
            get { return m_AssembliesToScan; }
            set { m_AssembliesToScan = value; }
        }
        public bool Autoscan { get; set; }
        public bool ReadAttributeBasedDeployments
        {
            get { return m_ReadAttributeBasedDeployments; }
            set { m_ReadAttributeBasedDeployments = value; }
        }
        public bool ReadDeploymentHosts
        {
            get { return m_ReadDeploymentHosts; }
            set { m_ReadDeploymentHosts = value; }
        }
    }
}
